import os
import json
import glob
from ffmpy import FFmpeg
import sys

# 搜索 电脑版 bilibili 下载路径下的所有文件，返回视频索引相关信息的列表
def get_video_info(source_video_fold):
    # 定义视频信息列表，存储所有视频的相关要素信息
    source_video_info=[]
    # 得到视频下载路径下所有子文件夹下的 .videoInfo文件列表，用于获取视频信息
    video_info_file_list=glob.glob(source_video_fold+'\\*\\.videoInfo')
    # 初始化视频信息文件名为空，存储单个 .videoInfo文件名
    video_info_file=''
    # 去除文本内容中的特殊字符串，定义映射表，特殊字符转换为 ^
    intab='~`!@#$%^&*()+=[]{}|?/><.:;"\\\t\n'
    outab='^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^'
    transtab=str.maketrans(intab,outab)

    # 遍历每个 .videoInfo 文件，提取其内容
    for video_info_file in video_info_file_list:
        # 得到 单个videoInfo 文件名柄
        video_info_file_handle=open(video_info_file,'r',encoding='utf-8')
        # 得到 单个videoInfo 文件的内容，转换为字典或字典列表
        video_info_dict=json.load(video_info_file_handle)
        # 得到当前视频所属的 分组名称 课程名
        video_group_title=str(video_info_dict['groupTitle'])
        temp_title=video_group_title.translate(transtab)
        video_group_title=temp_title.replace('^','')    #删除 ^
        # 得到当前视频的 名称 章节名
        video_title=video_info_dict['title']
        # 得到当前视频的 分组ID ID相同的属于同一课程
        video_group_id=video_info_dict['groupId']
        # 得到当前视频的 ID 视频所在的文件夹名称
        video_id=str(video_info_dict['itemId'])
        # 得到当前视频的 章节序号
        video_sn=str(video_info_dict['p']).zfill(3)
        # 得到当前视频的 所在路径
        video_path = os.path.dirname(video_info_dict['coverPath'])
        # 得到当前视频目录下的 视频文件名列表
        video_file_list=glob.glob(video_path+'\\*.m4s')
        # 比较视频文件大小，大的为视频，小的为音频
        if os.path.getsize(video_file_list[0])>os.path.getsize(video_file_list[1]):
            video_file=video_file_list[0]
            audio_file=video_file_list[1]
        else:
            video_file=video_file_list[1]
            audio_file=video_file_list[0]
        source_video_info.append([video_group_id,
                                  video_group_title,
                                  video_id,
                                  video_sn,
                                  video_title,
                                  video_file,
                                  audio_file])
        video_info_file_handle.close()

    # 将视频信息存入临时文件，用于调试阶段
    minfo=sorted(source_video_info,key=lambda aa:(aa[0],aa[3]))
    infofile=open('infofile.txt','w',encoding='utf-8')
    for strlst in minfo:
        infofile.write(str(strlst)+'\n')
    infofile.close()
    return minfo

# 对下载的原始文件进行 格式破解，返回已破解的文件名，与原文件名仅后缀不同
def decode_source_file(source_file):
    # 初始化已破解文件名的列表
    decoded_file=[]
    # 音视频文件名，去掉后缀
    video_file=os.path.splitext(source_file[0])
    audio_file=os.path.splitext(source_file[1])
    # 已破解文件名与源文件名相对应，仅后缀名变化，一个为视频，一个为音频
    decoded_file.append(video_file[0]+'.mp4')
    decoded_file.append(audio_file[0]+'.mp3')
    # 对 video 文件进行破解，删除源文件前 8 个字节内容
    with open(source_file[0],'rb') as input_file:
        with open(decoded_file[0],'wb') as output_file:
            input_file.seek(9,0)
            while True:
                mbuffer=input_file.read(1024)
                if not mbuffer:
                    break
                output_file.write(mbuffer)
    # 对 audio 文件进行破解，删除源文件前 8 个字节内容
    with open(source_file[1],'rb') as input_file:
        with open(decoded_file[1],'wb') as output_file:
            input_file.seek(9,0)
            while True:
                mbuffer=input_file.read(1024)
                if not mbuffer:
                    break
                output_file.write(mbuffer)
    # 返回 2 个元素的列表，包含已破解的 video 和 audio 文件
    return decoded_file

# 对已破解的 video 和 audio 文件合并为单一 video 文件，并删除已破解文件
def merge_decoded_file(decoded_file,merged_file):
    # 如果合并后的文件不存在，则进行合并操作
    if os.path.exists(merged_file)==False:
        merge_operator=FFmpeg(inputs={decoded_file[0]:None,decoded_file[1]:None},
                              outputs={merged_file:'-vcodec copy -acodec copy'})
        merge_operator.run()
    # 删除已破解的 2 个源文件
    os.remove(decoded_file[0])
    os.remove(decoded_file[1])

def get_mp4(source_video_fold,target_mp4_fold):
    source_video_info=[]
    video_info=[]
    source_file=[]
    decoded_file=[]
    mp4_file=''
    group_title=''
    group_num=0
    choice_num=-1
    group_title_set=set({})
    group_title_lst=[]
    source_video_info=get_video_info(source_video_fold)
    for video_info in source_video_info:
        group_title_lst.append(video_info[1])
    group_title_set=set(group_title_lst)
    group_title_lst=list(group_title_set)
    print('-------------------------------------------')
    print('检索到的课程列表如下：')
    for group_title in group_title_lst:
        print(str(group_num+1)+' '+group_title)
        group_num+=1
    print('-------------------------------------------')
    while True:
        try:
            choice_num=int(input('请选择您需要获取的课程序号（输入0获取所有课程，-1退出）（0-%d)： '% (group_num)))
            if (choice_num<-1 or choice_num>group_num):
                print('输入无效，请重新输入 0-%d 之间的数。'% group_num)
                continue
            elif choice_num==-1:
                print('退出！')
                return
            else:
                break
        except:
            print('输入无效，请重新输入整数！')
            continue
    for video_info in source_video_info:
        source_file=[]
        group_id=video_info[0]
        group_title=video_info[1]
        video_sn=str(video_info[3])
        video_title=video_info[4]
        video_file=video_info[5]
        audio_file=video_info[6]
        target_group_fold=target_mp4_fold+'\\'+group_title
        mp4_file=target_group_fold+'\\'+video_sn+'_'+video_title+'.mp4'
        source_file.append(video_file)
        source_file.append(audio_file)
        if choice_num==0:
            if os.path.exists(target_group_fold) == False:
                os.mkdir(target_group_fold)
            decoded_file= decode_source_file(source_file)
            merge_decoded_file(decoded_file,mp4_file)
        elif group_title==group_title_lst[choice_num-1]:
            if os.path.exists(target_group_fold) == False:
                os.mkdir(target_group_fold)
            decoded_file = decode_source_file(source_file)
            merge_decoded_file(decoded_file, mp4_file)
        else:
            continue


if __name__ == '__main__':
    # 电脑版bilibili设置的视频下载路径文件夹

    # source_video_fold = 'D:\\006 bilibili'
    # target_mp4_fold='D:\\000\\bilibili'

    source_video_fold=input('请输入电脑版bilibili的缓存视频所在绝对路径： ')
    if os.path.exists(source_video_fold)==False:
        print('输入的缓存视频路径无效！')
        sys.exit()
    target_mp4_fold=input('请输入用于存放 mp4 文件的绝对路径： ')
    if os.path.exists(target_mp4_fold)==False:
        print('用于存入 mp4 文件的绝对路径无效！')
        sys.exit()
    get_mp4(source_video_fold,target_mp4_fold)
    instr=input(' mp4 视频转换完成，任意键退出')
